﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Numerics;
using System.Text;
using ParserGenerator.Collections;
using ParserGenerator.Exceptions;
using ParserGenerator.Extensions;
using ParserGenerator.Input;
using ParserGenerator.Output;

namespace ParserGenerator.SyntacticAnalysis
{
    [Serializable]
    public class CSharpProgramDescriptionWriter : SyntaxTreeNodeWriter
    {
        public static new readonly CSharpProgramDescriptionWriter Instance = new CSharpProgramDescriptionWriter();

        private SyntaxTreeNodeTypeReducer _currentNodeTypeReducer;
        private ClassDeclarator _currentClassDeclarator;

        internal CSharpProgramDescriptionWriter()
        {
        }

        internal CSharpProgramDescriptionWriter(GrammarDeclaration grammar)
        {
            if (grammar == null)
                throw new ArgumentNullException(nameof(grammar));
            _currentNodeTypeReducer = new SyntaxTreeNodeTypeReducer(grammar);
        }

        private static bool IsKeyword(string name)
        {
            if (name == null)
                throw new ArgumentNullException(nameof(name));
            switch (name)
            {
                case "abstract":
                case "add":
                case "alias":
                case "as":
                case "ascending":
                case "async":
                case "await":
                case "base":
                case "bool":
                case "break":
                case "by":
                case "byte":
                case "case":
                case "catch":
                case "char":
                case "checked":
                case "class":
                case "const":
                case "continue":
                case "decimal":
                case "default":
                case "delegate":
                case "descending":
                case "do":
                case "double":
                case "dynamic":
                case "else":
                case "enum":
                case "equals":
                case "event":
                case "explicit":
                case "extern":
                case "false":
                case "finally":
                case "fixed":
                case "float":
                case "for":
                case "foreach":
                case "get":
                case "global":
                case "goto":
                case "group":
                case "if":
                case "implicit":
                case "in":
                case "int":
                case "interface":
                case "internal":
                case "into":
                case "is":
                case "join":
                case "let":
                case "lock":
                case "long":
                case "nameof":
                case "namespace":
                case "new":
                case "null":
                case "object":
                case "on":
                case "operator":
                case "orderby":
                case "out":
                case "override":
                case "params":
                case "partial":
                case "private":
                case "protected":
                case "public":
                case "readonly":
                case "ref":
                case "remove":
                case "return":
                case "sbyte":
                case "sealed":
                case "select":
                case "short":
                case "sizeof":
                case "stackalloc":
                case "static":
                case "string":
                case "struct":
                case "switch":
                case "this":
                case "throw":
                case "true":
                case "try":
                case "typeof":
                case "uint":
                case "ulong":
                case "unchecked":
                case "unsafe":
                case "ushort":
                case "using":
                case "var":
                case "virtual":
                case "void":
                case "volatile":
                case "when":
                case "where":
                case "while":
                case "yield":
                    return true;
            }
            return false;
        }

        public override void WriteIdentifier(string n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (IsKeyword(n))
                writer.Write("@{0}", n);
            else
                writer.Write(n);
        }

        public override void WriteFloatLiteral(double n, IndentedWriter writer, bool allowAsInt)
        {
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (double.IsPositiveInfinity(n))
                writer.Write("global::System.Double.PositiveInfinity");
            else if (double.IsNegativeInfinity(n))
                writer.Write("global::System.Double.NegativeInfinity");
            else if (double.IsNaN(n))
                writer.Write("global::System.Double.NaN");
            else
            {
                string str = n.ToString(CultureInfo.InvariantCulture).ToLowerInvariant();
                str = str.Replace("e+", "e");
                if (!allowAsInt && str.IndexOfAny(new[] { '.', 'e' }) == -1)
                    writer.Write("{0}.0", str);
                else
                    writer.Write(str);
            }
        }

        public override void WriteComplexLiteral(Complex n, IndentedWriter writer)
        {
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("new global::System.Numerics.Complex(");
            WriteFloatLiteral(n.Real, writer, false);
            writer.Write(", ");
            WriteFloatLiteral(n.Imaginary, writer, false);
            writer.Write(")");
        }

        public override string Escape(int c, char delimiter)
        {
            if (c < CharExtensions.MinChar || c > CharExtensions.MaxChar)
                throw new ArgumentOutOfRangeException(nameof(c), SR.InvalidUnicodeCodepoint);
            if (delimiter != '\'' && delimiter != '"')
                throw new ArgumentException(SR.DelimiterMustBeSingleOrDoubleQuote, nameof(delimiter));
            switch (c)
            {
                case '\0': return "\\0";
                case '\a': return "\\a";
                case '\b': return "\\b";
                case '\t': return "\\t";
                case '\n': return "\\n";
                case '\v': return "\\v";
                case '\f': return "\\f";
                case '\r': return "\\r";
                case '\\': return "\\\\";
                case '\'': return delimiter == '\'' ? "\\'" : "'";
                case '"': return delimiter == '"' ? "\\\"" : "\"";
                default:
                    if (' ' <= c && c <= 0x7E)
                        return "" + (char)c;
                    else if (0 <= c && c <= 0xFFFF)
                        return string.Format("\\u{0:x4}", c);
                    else
                        return string.Format("\\U{0:x8}", c);
            }
        }

        public override void WriteCharLiteral(int n, IndentedWriter writer)
        {
            if (n < CharExtensions.MinChar || n > CharExtensions.MaxChar)
                throw new ArgumentOutOfRangeException(nameof(n), SR.InvalidUnicodeCodepoint);
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (n <= 0xFFFF)
                base.WriteCharLiteral(n, writer);
            else
            {
                WriteIntegerLiteral(new BigInteger(n), writer);
                writer.Write(" ");
                writer.Write("/* ");
                base.WriteCharLiteral(n, writer);
                writer.Write(" */");
            }
        }

        public override void WriteQuoteStringLiteral(string n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            int[] cs = n.GetCodepoints();
            if (cs.Length == 1 && 0 <= cs[0] && cs[0] <= 0xFFFF)
            {
                WriteCharLiteral(cs[0], writer);
                return;
            }
            base.WriteStringLiteral(n, writer);
        }

        public override void WriteByteStringLiteral(IEnumerable<byte> n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("new global::System.Collections.ObjectModel.ReadOnlyCollection<byte>(new byte[] { ");
            bool first = true;
            foreach (byte value in n)
            {
                if (first)
                    first = false;
                else
                    writer.Write(", ");
                WriteIntegerLiteral(new BigInteger(value), writer);
            }
            if (!first)
                writer.Write(" ");
            writer.Write("})");
        }

        public override void WriteQuoteByteStringLiteral(IEnumerable<byte> n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteByteStringLiteral(n, writer);
        }

        public override void WriteStringSeriesExpression(StringSeriesExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            StringBuilder sb = new StringBuilder();
            foreach (StringLiteral item in n.Values)
                sb.Append(item.Value);
            WriteStringLiteral(sb.ToString(), writer);
        }

        public override void WriteByteStringSeriesExpression(ByteStringSeriesExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            List<byte> bsb = new List<byte>();
            foreach (ByteStringLiteral item in n.Values)
                bsb.AddRange(item.Value);
            WriteByteStringLiteral(bsb, writer);
        }

        public override void WriteQuoteStringSeriesExpression(QuoteStringSeriesExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            StringBuilder sb = new StringBuilder();
            foreach (StringLiteral item in n.Values)
                sb.Append(item.Value);
            WriteQuoteStringLiteral(sb.ToString(), writer);
        }

        public override void WriteQuoteByteStringSeriesExpression(QuoteByteStringSeriesExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            List<byte> bsb = new List<byte>();
            foreach (ByteStringLiteral item in n.Values)
                bsb.AddRange(item.Value);
            WriteQuoteByteStringLiteral(bsb, writer);
        }

        public override void WriteNameReferenceExpression(NameReferenceExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            string name = n.Name.Name;
            if (name == "self")
            {
                writer.Write("this");
                return;
            }
            base.WriteNameReferenceExpression(n, writer, d);
        }

        public override void WriteTupleExpression(TupleExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::System.Tuple.Create(");
            int counter = 1;
            for (int i = 0; i < n.Elements.Count; i++)
            {
                WriteExpression(n.Elements[i], writer);
                if (i != n.Elements.Count - 1)
                    writer.Write(", ");
                if (i != n.Elements.Count - 1 && (i + 1) % 7 == 0)
                {
                    writer.Write("global::System.Tuple.Create(");
                    counter++;
                }
            }
            for (int i = 0; i < counter; i++)
                writer.Write(")");
        }

        public override void WriteValueTupleExpression(ValueTupleExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::System.ValueTuple.Create(");
            int counter = 1;
            for (int i = 0; i < n.Elements.Count; i++)
            {
                WriteExpression(n.Elements[i], writer);
                if (i != n.Elements.Count - 1)
                    writer.Write(", ");
                if (i != n.Elements.Count - 1 && (i + 1) % 7 == 0)
                {
                    writer.Write("global::System.ValueTuple.Create(");
                    counter++;
                }
            }
            for (int i = 0; i < counter; i++)
                writer.Write(")");
        }

        public override void WriteListExpression(ListExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::ParserGenerator.Collections.Sequence.Create(");
            for (int i = 0; i < n.Elements.Count; i++)
            {
                if (i > 0)
                    writer.Write(", ");
                WriteExpression(n.Elements[i], writer);
            }
            writer.Write(")");
        }

        public override void WriteSetExpression(SetExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::ParserGenerator.Collections.ReadOnlySet.Create(");
            for (int i = 0; i < n.Elements.Count; i++)
            {
                if (i > 0)
                    writer.Write(", ");
                WriteExpression(n.Elements[i], writer);
            }
            writer.Write(")");
        }

        public override void WriteMapExpression(MapExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::ParserGenerator.Collections.ReadOnlyDictionary.Create(");
            for (int i = 0; i < n.Elements.Count; i++)
            {
                if (i > 0)
                    writer.Write(", ");
                writer.Write("global::ParserGenerator.Collections.KeyValuePair.Create(");
                WriteExpression(n.Elements[i].Key, writer);
                writer.Write(", ");
                WriteExpression(n.Elements[i].Value, writer);
                writer.Write(")");
            }
            writer.Write(")");
        }

        public override void WriteComprehensionList(ComprehensionList n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            int column = writer.Column;
            int output = 0;
            foreach (Comprehension item in n.Items)
            {
                if (item.IsAsync)
                {
                    RuntimeErrorException e = new RuntimeErrorException(SR.CSharpHasNoAsyncForComprehension);
                    e.AddLocation(n.Context);
                    e.AddLocation(item.Context);
                    throw e;
                }
                if (output > 0)
                {
                    writer.Write("\n");
                    writer.SpaceTo(column);
                }
                writer.Write("from ");
                WriteExpression(item.Enumerator, writer);
                writer.Write(" in ");
                WriteExpression(item.Enumerable, writer);
                foreach (ComprehensionFilter filter in item.Filters.Items)
                {
                    writer.Write("\n");
                    writer.SpaceTo(column);
                    writer.Write("where ");
                    WriteExpression(filter.Condition, writer);
                }
                output++;
            }
            writer.Write("\n");
            writer.SpaceTo(column);
            writer.Write("select ");
        }

        public override void WriteGeneratorExpression(GeneratorExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("(");
            WriteComprehensionList(n.Comprehensions, writer);
            WriteExpression(n.Element, writer);
            writer.Write(")");
        }

        public override void WriteListComprehensionExpression(ListComprehensionExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::ParserGenerator.Collections.Sequence.New(");
            WriteComprehensionList(n.Comprehensions, writer);
            WriteExpression(n.Element, writer);
            writer.Write(")");
        }

        public override void WriteSetComprehensionExpression(SetComprehensionExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::ParserGenerator.Collections.ReadOnlySet.New(");
            WriteComprehensionList(n.Comprehensions, writer);
            WriteExpression(n.Element, writer);
            writer.Write(")");
        }

        public override void WriteMapComprehensionExpression(MapComprehensionExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::ParserGenerator.Collections.ReadOnlyDictionary.New(");
            WriteComprehensionList(n.Comprehensions, writer);
            writer.Write("global::ParserGenerator.Collections.KeyValuePair.Create(");
            WriteExpression(n.Key, writer);
            writer.Write(", ");
            WriteExpression(n.Value, writer);
            writer.Write("))");
        }

        public override void WriteCallExpression(CallExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (n.Operand.IsNameReference)
            {
                string name = n.Operand.AsNameReference.Name.Name;
                if (name == "print")
                {
                    writer.Write("global::System.Console.WriteLine");
                    WriteArgumentList(n.Arguments, writer);
                    return;
                }
                if (name == "range" || name == "xrange")
                {
                    writer.Write("global::System.Linq.Enumerable.Range");
                    var args = n.Arguments;
                    if (args.Items.Count == 1)
                    {
                        var zero = Expression.NewInteger(IntegerLiteral.New(0));
                        var arg = Argument.NewPositional(zero);
                        args = args.WithItems(Sequence.Prepend(arg, args.Items));
                    }
                    WriteArgumentList(args, writer);
                    return;
                }
                if (name == "super")
                {
                    writer.Write("base");
                    return;
                }
            }
            base.WriteCallExpression(n, writer, d);
        }

        public override void WriteSubscriptList(SubscriptList n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (n.HasTrailingComma && n.Items.All(x => x.IsIndex))
            {
                var t = Expression.NewTuple(Sequence.New(from x in n.Items select x.AsIndex.Value));
                WriteExpression(t, writer);
                return;
            }
            base.WriteSubscriptList(n, writer);
        }

        public override void WriteDefaultExpression(DefaultExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (n.Type.IsList || n.Type.IsNonEmptyList)
            {
                const int prec = PostfixExpressionOpPrecedence;
                if (d > prec)
                    writer.Write("(");
                writer.Write("global::ParserGenerator.Collections.Sequence.GetEmpty<");
                if (n.Type.IsList)
                    WriteTypeExpression(n.Type.AsList.ElementType, writer, -1);
                else
                    WriteTypeExpression(n.Type.AsNonEmptyList.ElementType, writer, -1);
                writer.Write(">()");
                if (d > prec)
                    writer.Write(")");
                return;
            }
            base.WriteDefaultExpression(n, writer, d);
        }

        public override void WriteMutableListExpression(MutableListExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::ParserGenerator.Collections.List.Create(");
            for (int i = 0; i < n.Elements.Count; i++)
            {
                if (i > 0)
                    writer.Write(", ");
                WriteExpression(n.Elements[i], writer);
            }
            writer.Write(")");
        }

        public override void WriteMutableSetExpression(MutableSetExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::ParserGenerator.Collections.HashSet.Create(");
            for (int i = 0; i < n.Elements.Count; i++)
            {
                if (i > 0)
                    writer.Write(", ");
                WriteExpression(n.Elements[i], writer);
            }
            writer.Write(")");
        }

        public override void WriteMutableMapExpression(MutableMapExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::ParserGenerator.Collections.Dictionary.Create(");
            for (int i = 0; i < n.Elements.Count; i++)
            {
                if (i > 0)
                    writer.Write(", ");
                writer.Write("global::ParserGenerator.Collections.KeyValuePair.Create(");
                WriteExpression(n.Elements[i].Key, writer);
                writer.Write(", ");
                WriteExpression(n.Elements[i].Value, writer);
                writer.Write(")");
            }
            writer.Write(")");
        }

        public override void WriteMutableListComprehensionExpression(MutableListComprehensionExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::ParserGenerator.Collections.List.New(");
            WriteComprehensionList(n.Comprehensions, writer);
            WriteExpression(n.Element, writer);
            writer.Write(")");
        }

        public override void WriteMutableSetComprehensionExpression(MutableSetComprehensionExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::ParserGenerator.Collections.HashSet.New(");
            WriteComprehensionList(n.Comprehensions, writer);
            WriteExpression(n.Element, writer);
            writer.Write(")");
        }

        public override void WriteMutableMapComprehensionExpression(MutableMapComprehensionExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::ParserGenerator.Collections.Dictionary.New(");
            WriteComprehensionList(n.Comprehensions, writer);
            writer.Write("global::ParserGenerator.Collections.KeyValuePair.Create(");
            WriteExpression(n.Key, writer);
            writer.Write(", ");
            WriteExpression(n.Value, writer);
            writer.Write("))");
        }

        public override void WriteIsInstanceOfExpression(IsInstanceOfExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (n.Operand.IsThisReference)
            {
                WriteExpression(n.Operand, writer);
                writer.Write(" is ");
                WriteTypeExpression(n.Type, writer);
                return;
            }
            writer.Write("((");
            WriteExpression(n.Operand, writer);
            writer.Write(") is ");
            WriteTypeExpression(n.Type, writer);
            writer.Write(")");
        }

        public override void WriteAsInstanceOfExpression(AsInstanceOfExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (n.Operand.IsThisReference)
            {
                writer.Write("(");
                WriteTypeExpression(n.Type, writer);
                writer.Write(")");
                WriteExpression(n.Operand, writer);
                return;
            }
            writer.Write("((");
            WriteTypeExpression(n.Type, writer);
            writer.Write(")(");
            WriteExpression(n.Operand, writer);
            writer.Write("))");
        }

        public override void WritePowerExpression(PowerExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::System.Math.Pow(");
            WriteExpression(n.Left, writer);
            writer.Write(", ");
            WriteExpression(n.Right, writer);
            writer.Write(")");
        }

        public override void WriteMatrixMultiplyExpression(MatrixMultiplyExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            RuntimeErrorException e = new RuntimeErrorException(SR.CSharpHasNoMatrixMultiplyOp);
            e.AddLocation(n.Context);
            throw e;
        }

        public override void WriteFloorDivideExpression(FloorDivideExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("((");
            WriteExpression(n.Left, writer);
            writer.Write(") / (");
            WriteExpression(n.Right, writer);
            writer.Write("))");
        }

        public override void WriteDivideExpression(DivideExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("((");
            WriteExpression(n.Left, writer);
            writer.Write(") / ");
            WriteExpression(Expression.NewAsInstanceOf(n.Right, TypeExpression.NewFloat64()), writer);
            writer.Write(")");
        }

        public override void WriteUnsignedRightShiftExpression(UnsignedRightShiftExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            var cast1 = Expression.NewAsInstanceOf(n.Left, TypeExpression.NewUInt());
            var shift = Expression.NewRightShift(cast1, n.Right);
            var cast2 = Expression.NewAsInstanceOf(shift, TypeExpression.NewInt());
            WriteExpression(cast2, writer, d);
        }

        public override void WriteRelationalExpression(RelationalExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (n.OpValuePairs.Count == 1)
            {
                RelationalOpValuePair p = n.OpValuePairs[0];
                switch (p.Op)
                {
                    case RelationalOp.Is:
                        if (p.Value.IsNull)
                        {
                            WriteExpression(n.Left, writer);
                            writer.Write(" == null");
                            break;
                        }
                        writer.Write("global::System.Object.Equals(");
                        WriteExpression(n.Left, writer);
                        writer.Write(", ");
                        WriteExpression(p.Value, writer);
                        writer.Write(")");
                        break;
                    case RelationalOp.IsNot:
                        if (p.Value.IsNull)
                        {
                            WriteExpression(n.Left, writer);
                            writer.Write(" != null");
                            break;
                        }
                        writer.Write("(!global::System.Object.Equals(");
                        WriteExpression(n.Left, writer);
                        writer.Write(", ");
                        WriteExpression(p.Value, writer);
                        writer.Write("))");
                        break;
                    case RelationalOp.In:
                    case RelationalOp.NotIn:
                        var access = Expression.NewAccess(p.Value, Identifier.New("Contains"));
                        var call = Expression.NewCall(access, ArgumentList.New(Sequence.Create(Argument.NewPositional(n.Left))));
                        var node = call;
                        if (p.Op == RelationalOp.NotIn)
                            node = Expression.NewLogicalNot(node);
                        WriteExpression(node, writer, d);
                        break;
                    default:
                        base.WriteRelationalExpression(n, writer, d);
                        break;
                }
                return;
            }
            Expression result = null;
            Expression prev = n.Left;
            foreach (RelationalOpValuePair pair in n.OpValuePairs)
            {
                Expression cond = Expression.NewRelational(prev, Sequence.Create(pair));
                if (result == null)
                    result = cond;
                else
                    result = Expression.NewLogicalAnd(result, cond);
                prev = pair.Value;
            }
            WriteExpression(result, writer, d);
        }

        public override void WriteConditionalExpression(ConditionalExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("((");
            WriteExpression(n.Test, writer);
            writer.Write(") ? (");
            WriteExpression(n.Body, writer);
            writer.Write(") : (");
            WriteExpression(n.OrElse, writer);
            writer.Write("))");
        }

        public override void WriteLambdaExpression(LambdaExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("((");
            WriteParameterList(n.Parameters, writer);
            writer.Write(") => (");
            WriteExpression(n.Body, writer);
            writer.Write("))");
        }

        public override void WriteYieldFromExpression(YieldFromExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            string name = "_t" + n.GetHashCode();
            writer.Write("foreach (var {0} in ", name);
            WriteExpression(n.Value, writer);
            writer.Write(")\n");
            writer.OpenBlock();
            writer.Write("yield return {0}", name);
            writer.CloseBlock();
        }

        public override void WriteYieldExpression(YieldExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (n.Value == null)
            {
                RuntimeErrorException e = new RuntimeErrorException(SR.CSharpYieldMustReturnValue);
                e.AddLocation(n.Context);
                throw e;
            }
            writer.Write("yield return ");
            WriteExpression(n.Value, writer);
        }

        public override void WriteYieldBreakExpression(YieldBreakExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("yield break");
        }

        public override void WriteUnpackExpression(UnpackExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            RuntimeErrorException e = new RuntimeErrorException(SR.CSharpHasNoSequenceUnpackingOp);
            e.AddLocation(n.Context);
            throw e;
        }

        public override void WriteInt8TypeExpression(Int8TypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("sbyte");
        }

        public override void WriteInt16TypeExpression(Int16TypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("short");
        }

        public override void WriteInt32TypeExpression(Int32TypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("int");
        }

        public override void WriteInt64TypeExpression(Int64TypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("long");
        }

        public override void WriteUInt8TypeExpression(UInt8TypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("byte");
        }

        public override void WriteUInt16TypeExpression(UInt16TypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("ushort");
        }

        public override void WriteUInt32TypeExpression(UInt32TypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("uint");
        }

        public override void WriteUInt64TypeExpression(UInt64TypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("ulong");
        }

        public override void WriteFloat32TypeExpression(Float32TypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("float");
        }

        public override void WriteFloat64TypeExpression(Float64TypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("double");
        }

        public override void WriteChar8TypeExpression(Char8TypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("byte");
        }

        public override void WriteChar16TypeExpression(Char16TypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("char");
        }

        public override void WriteChar32TypeExpression(Char32TypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("int");
        }

        public override void WriteIntPtrTTypeExpression(IntPtrTTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::System.IntPtr");
        }

        public override void WriteUIntPtrTTypeExpression(UIntPtrTTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::System.UIntPtr");
        }

        public override void WriteSizeTTypeExpression(SizeTTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::System.UIntPtr");
        }

        public override void WriteSSizeTTypeExpression(SSizeTTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::System.IntPtr");
        }

        public override void WritePtrDiffTTypeExpression(PtrDiffTTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::System.IntPtr");
        }

        public override void WriteUnitTypeExpression(UnitTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("void");
        }

        public override void WriteTupleTypeExpression(TupleTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::System.Tuple");
            writer.Write("<");
            int counter = 1;
            for (int i = 0; i < n.ElementTypes.Count; i++)
            {
                WriteTypeExpression(n.ElementTypes[i], writer);
                if (i != n.ElementTypes.Count - 1)
                    writer.Write(", ");
                if (i != n.ElementTypes.Count - 1 && (i + 1) % 7 == 0)
                {
                    writer.Write("global::System.Tuple");
                    writer.Write("<");
                    counter++;
                }
            }
            for (int i = 0; i < counter; i++)
                writer.Write(">");
        }

        public override void WriteValueTupleTypeExpression(ValueTupleTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::System.ValueTuple");
            writer.Write("<");
            int counter = 1;
            for (int i = 0; i < n.ElementTypes.Count; i++)
            {
                WriteTypeExpression(n.ElementTypes[i], writer);
                if (i != n.ElementTypes.Count - 1)
                    writer.Write(", ");
                if (i != n.ElementTypes.Count - 1 && (i + 1) % 7 == 0)
                {
                    writer.Write("global::System.ValueTuple");
                    writer.Write("<");
                    counter++;
                }
            }
            for (int i = 0; i < counter; i++)
                writer.Write(">");
        }

        public override void WriteListTypeExpression(ListTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::ParserGenerator.Collections.Sequence<");
            WriteTypeExpression(n.ElementType, writer);
            writer.Write(">");
        }

        public override void WriteSetTypeExpression(SetTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::ParserGenerator.Collections.ReadOnlySet<");
            WriteTypeExpression(n.ElementType, writer);
            writer.Write(">");
        }

        public override void WriteMapTypeExpression(MapTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::System.Collections.ObjectModel.ReadOnlyDictionary<");
            WriteTypeExpression(n.KeyType, writer);
            writer.Write(", ");
            WriteTypeExpression(n.ValueType, writer);
            writer.Write(">");
        }

        public override void WriteUnboundedGenericTypeExpression(UnboundedGenericTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteTypeExpression(n.Operand, writer);
            writer.Write("<");
            for (int i = 1; i < n.Arity; i++)
                writer.Write(",");
            writer.Write(">");
        }

        public override void WriteBoundedGenericTypeExpression(BoundedGenericTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteTypeExpression(n.Operand, writer);
            writer.Write("<");
            for (int i = 0; i < n.Arguments.Count; i++)
            {
                if (i > 0)
                    writer.Write(", ");
                WriteTypeExpression(n.Arguments[i], writer);
            }
            writer.Write(">");
        }

        public override void WriteNonNegativeInt8TypeExpression(NonNegativeInt8TypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("sbyte");
        }

        public override void WriteNonNegativeInt16TypeExpression(NonNegativeInt16TypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("short");
        }

        public override void WriteNonNegativeInt32TypeExpression(NonNegativeInt32TypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("int");
        }

        public override void WriteNonNegativeInt64TypeExpression(NonNegativeInt64TypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("long");
        }

        public override void WriteNonNegativeFloat32TypeExpression(NonNegativeFloat32TypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("float");
        }

        public override void WriteNonNegativeFloat64TypeExpression(NonNegativeFloat64TypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("double");
        }

        public override void WriteNonNegativeSByteTypeExpression(NonNegativeSByteTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("sbyte");
        }

        public override void WriteNonNegativeShortTypeExpression(NonNegativeShortTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("short");
        }

        public override void WriteNonNegativeIntTypeExpression(NonNegativeIntTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("int");
        }

        public override void WriteNonNegativeLongTypeExpression(NonNegativeLongTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("long");
        }

        public override void WriteNonNegativeIntPtrTTypeExpression(NonNegativeIntPtrTTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::System.IntPtr");
        }

        public override void WriteNonNegativeSSizeTTypeExpression(NonNegativeSSizeTTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::System.IntPtr");
        }

        public override void WriteNonNegativePtrDiffTTypeExpression(NonNegativePtrDiffTTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::System.IntPtr");
        }

        public override void WriteNonNegativeFloatTypeExpression(NonNegativeFloatTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("float");
        }

        public override void WriteNonNegativeDoubleTypeExpression(NonNegativeDoubleTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("double");
        }

        public override void WriteNonNegativeDecimalTypeExpression(NonNegativeDecimalTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("decimal");
        }

        public override void WritePositiveInt8TypeExpression(PositiveInt8TypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("sbyte");
        }

        public override void WritePositiveInt16TypeExpression(PositiveInt16TypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("short");
        }

        public override void WritePositiveInt32TypeExpression(PositiveInt32TypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("int");
        }

        public override void WritePositiveInt64TypeExpression(PositiveInt64TypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("long");
        }

        public override void WritePositiveFloat32TypeExpression(PositiveFloat32TypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("float");
        }

        public override void WritePositiveFloat64TypeExpression(PositiveFloat64TypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("double");
        }

        public override void WritePositiveSByteTypeExpression(PositiveSByteTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("sbyte");
        }

        public override void WritePositiveShortTypeExpression(PositiveShortTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("short");
        }

        public override void WritePositiveIntTypeExpression(PositiveIntTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("int");
        }

        public override void WritePositiveLongTypeExpression(PositiveLongTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("long");
        }
        public override void WritePositiveIntPtrTTypeExpression(PositiveIntPtrTTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::System.IntPtr");
        }

        public override void WritePositiveSSizeTTypeExpression(PositiveSSizeTTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::System.IntPtr");
        }

        public override void WritePositiveDiffTTypeExpression(PositiveDiffTTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::System.IntPtr");
        }

        public override void WritePositiveFloatTypeExpression(PositiveFloatTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("float");
        }

        public override void WritePositiveDoubleTypeExpression(PositiveDoubleTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("double");
        }

        public override void WritePositiveDecimalTypeExpression(PositiveDecimalTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("decimal");
        }

        public override void WriteMutableListTypeExpression(MutableListTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::System.Collections.Generic.List<");
            WriteTypeExpression(n.ElementType, writer);
            writer.Write(">");
        }

        public override void WriteMutableSetTypeExpression(MutableSetTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::System.Collections.Generic.HashSet<");
            WriteTypeExpression(n.ElementType, writer);
            writer.Write(">");
        }

        public override void WriteMutableMapTypeExpression(MutableMapTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::System.Collections.Generic.Dictionary<");
            WriteTypeExpression(n.KeyType, writer);
            writer.Write(", ");
            WriteTypeExpression(n.ValueType, writer);
            writer.Write(">");
        }

        public override void WriteNonEmptyListTypeExpression(NonEmptyListTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::ParserGenerator.Collections.Sequence<");
            WriteTypeExpression(n.ElementType, writer);
            writer.Write(">");
        }

        public override void WriteNonEmptySetTypeExpression(NonEmptySetTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::ParserGenerator.Collections.ReadOnlySet<");
            WriteTypeExpression(n.ElementType, writer);
            writer.Write(">");
        }

        public override void WriteNonEmptyMapTypeExpression(NonEmptyMapTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::System.Collections.ObjectModel.ReadOnlyDictionary<");
            WriteTypeExpression(n.KeyType, writer);
            writer.Write(", ");
            WriteTypeExpression(n.ValueType, writer);
            writer.Write(">");
        }

        public override void WriteNonEmptyMutableListTypeExpression(NonEmptyMutableListTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::System.Collections.Generic.List<");
            WriteTypeExpression(n.ElementType, writer);
            writer.Write(">");
        }

        public override void WriteNonEmptyMutableSetTypeExpression(NonEmptyMutableSetTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::System.Collections.Generic.HashSet<");
            WriteTypeExpression(n.ElementType, writer);
            writer.Write(">");
        }

        public override void WriteNonEmptyMutableMapTypeExpression(NonEmptyMutableMapTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::System.Collections.Generic.Dictionary<");
            WriteTypeExpression(n.KeyType, writer);
            writer.Write(", ");
            WriteTypeExpression(n.ValueType, writer);
            writer.Write(">");
        }

        public override void WriteNullableTypeExpression(NullableTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteTypeExpression(n.OperandType, writer);
            if (_currentNodeTypeReducer == null || _currentNodeTypeReducer.IsValueType(n.OperandType))
                writer.Write("?");
        }

        public override void WriteFunctionTypeExpression(FunctionTypeExpression n, IndentedWriter writer, int d)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            List<TypeExpression> args = new List<TypeExpression>();
            bool isAction = false;
            while (true)
            {
                args.Add(n.ArgumentType);
                if (n.ResultType.IsFunction)
                    n = n.ResultType.AsFunction;
                else
                {
                    if (n.ResultType.IsUnit)
                        isAction = true;
                    else
                        args.Add(n.ResultType);
                    break;
                }
            }
            writer.Write("global::System.");
            writer.Write(isAction ? "Action" : "Func");
            writer.Write("<");
            for (int i = 0; i < args.Count; i++)
            {
                if (i > 0)
                    writer.Write(", ");
                WriteTypeExpression(args[i], writer);
            }
            writer.Write(">");
        }

        private void WriteDottedName(string name, IndentedWriter writer)
        {
            if (name == null)
                throw new ArgumentNullException(nameof(name));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            Identifier id = Identifier.New(name);
            id.CheckDottedName();
            bool isFirst = true;
            foreach (Identifier item in id.Items)
            {
                writer.Write(isFirst ? "" : ".");
                WriteIdentifier(item, writer);
                isFirst = false;
            }
        }

        public virtual bool WriteTypeDefinitions(GrammarDeclaration n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("namespace ");
            WriteDottedName(n.SyntacticAnalysisNamespace, writer);
            writer.Write("\n");
            writer.OpenCurlyBlock();
            SyntaxTreeNodeTypeReducer savedNodeTypeReducer = _currentNodeTypeReducer;
            _currentNodeTypeReducer = new SyntaxTreeNodeTypeReducer(n);
            bool isFirst = true;
            foreach (TypeDeclarator decl in _currentNodeTypeReducer.NodeTypeDeclarators)
            {
                if (isFirst)
                    isFirst = false;
                else
                    writer.Write("\n");
                WriteTypeDeclarator(decl, writer);
            }
            _currentNodeTypeReducer = savedNodeTypeReducer;
            writer.CloseCurlyBlock();
            return !isFirst;
        }

        public override void WriteGrammarDeclaration(GrammarDeclaration n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            base.WriteGrammarDeclaration(n, writer);
        }

        public override void WritePassStatement(PassStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write(";\n");
        }

        public override void WriteBreakStatement(BreakStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("break;\n");
        }

        public override void WriteContinueStatement(ContinueStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("continue;\n");
        }

        public override void WriteReturnStatement(ReturnStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("return");
            if (n.Value != null)
            {
                writer.Write(" ");
                WriteExpression(n.Value, writer);
            }
            writer.Write(";\n");
        }

        public override void WriteThrowStatement(ThrowStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("throw");
            if (n.Exception != null)
            {
                writer.Write(" ");
                WriteExpression(n.Exception, writer);
            }
            if (n.Cause != null)
            {
                RuntimeErrorException e = new RuntimeErrorException(SR.CSharpHasNoThrowFromStatement);
                e.AddLocation(n.Context);
                e.AddLocation(n.Cause.Context);
                throw e;
            }
            writer.Write(";\n");
        }

        public override void WriteAssertStatement(AssertStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("global::System.Diagnostics.Debug.Assert(");
            WriteExpression(n.Test, writer);
            if (n.Message != null)
            {
                writer.Write(", ");
                WriteExpression(n.Message, writer);
            }
            writer.Write(");\n");
        }

        public override void WriteDeleteStatement(DeleteStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            RuntimeErrorException e = new RuntimeErrorException(SR.CSharpHasNoDeleteStatement);
            e.AddLocation(n.Context);
            throw e;
        }

        public override void WriteGlobalStatement(GlobalStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            RuntimeErrorException e = new RuntimeErrorException(SR.CSharpHasNoGlobalStatement);
            e.AddLocation(n.Context);
            throw e;
        }

        public override void WriteNonlocalStatement(NonlocalStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            RuntimeErrorException e = new RuntimeErrorException(SR.CSharpHasNoNonlocalStatement);
            e.AddLocation(n.Context);
            throw e;
        }

        public override void WriteImportStatement(ImportStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            foreach (ImportBinding item in n.Bindings)
            {
                if (item.Alias != null)
                {
                    writer.Write("using ");
                    WriteIdentifier(item.Alias, writer);
                    writer.Write(" = ");
                    for (int i = 0; i < item.Name.Count; i++)
                    {
                        if (i > 0)
                            writer.Write(".");
                        WriteIdentifier(item.Name[i], writer);
                    }
                    writer.Write(";\n");
                }
                else
                {
                    if (item.Name.Count <= 1)
                        continue;
                    writer.Write("using ");
                    WriteIdentifier(item.Name[item.Name.Count - 1], writer);
                    writer.Write(" = ");
                    for (int i = 0; i < item.Name.Count; i++)
                    {
                        if (i > 0)
                            writer.Write(".");
                        WriteIdentifier(item.Name[i], writer);
                    }
                    writer.Write(";\n");
                }
            }
        }

        public override void WriteFromImportStatement(FromImportStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (n.Level != null)
            {
                RuntimeErrorException e = new RuntimeErrorException(SR.CSharpHasNoRelativeFromImportStatement);
                e.AddLocation(n.Context);
                throw e;
            }
            foreach (FromImportBinding item in n.Bindings)
            {
                writer.Write("using ");
                if (item.Alias == null)
                    WriteIdentifier(item.Name, writer);
                else
                    WriteIdentifier(item.Alias, writer);
                writer.Write(" = ");
                for (int i = 0; i < n.Module.Count; i++)
                {
                    if (i > 0)
                        writer.Write(".");
                    WriteIdentifier(n.Module[i], writer);
                }
                if (n.Module.Count > 0)
                    writer.Write(".");
                WriteIdentifier(item.Name, writer);
                writer.Write(";\n");
            }
        }

        public override void WriteFromImportAllStatement(FromImportAllStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (n.Level != null)
            {
                RuntimeErrorException e = new RuntimeErrorException(SR.CSharpHasNoRelativeFromImportStatement);
                e.AddLocation(n.Context);
                throw e;
            }
            writer.Write("using ");
            for (int i = 0; i < n.Module.Count; i++)
            {
                if (i > 0)
                    writer.Write(".");
                WriteIdentifier(n.Module[i], writer);
            }
            writer.Write(";\n");
        }

        public override void WriteLetStatement(LetStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (n.Type == null)
                writer.Write("var");
            else
                WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            WriteIdentifier(n.Variable, writer);
            writer.Write(" = ");
            WriteExpression(n.Value, writer);
            writer.Write(";\n");
        }

        public override void WriteVarStatement(VarStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (n.Type == null)
                writer.Write("var");
            else
                WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            WriteIdentifier(n.Variable, writer);
            writer.Write(" = ");
            WriteExpression(n.Value, writer);
            writer.Write(";\n");
        }

        public override void WriteConstStatement(ConstStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (n.Type == null)
                writer.Write("var");
            else
            {
                writer.Write("const ");
                WriteTypeExpression(n.Type, writer);
            }
            writer.Write(" ");
            WriteIdentifier(n.Variable, writer);
            writer.Write(" = ");
            WriteExpression(n.Value, writer);
            writer.Write(";\n");
        }

        public override void WriteExprStatement(ExprStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteExpression(n.Value, writer);
            writer.Write(";\n");
        }

        public override void WriteAssignStatement(AssignStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            for (int i = 0; i < n.Targets.Count; i++)
            {
                if (i > 0)
                    writer.Write(" = ");
                WriteExpression(n.Targets[i], writer);
            }
            writer.Write(" = ");
            WriteExpression(n.Value, writer);
            writer.Write(";\n");
        }

        public override void WriteAnnotatedAssignStatement(AnnotatedAssignStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (!n.Target.IsNameReference || !n.IsSimple)
            {
                RuntimeErrorException e = new RuntimeErrorException(SR.CSharpHasNoComplexAnnotatedAssignmentStatement);
                e.AddLocation(n.Context);
                throw e;
            }
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            WriteExpression(n.Target, writer);
            if (n.Value != null)
            {
                writer.Write(" = ");
                WriteExpression(n.Value, writer);
            }
            writer.Write(";\n");
        }

        public override void WriteAddAssignStatement(AddAssignStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteExpression(n.Target, writer);
            writer.Write(" += ");
            WriteExpression(n.Value, writer);
            writer.Write(";\n");
        }

        public override void WriteSubtractAssignStatement(SubtractAssignStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteExpression(n.Target, writer);
            writer.Write(" -= ");
            WriteExpression(n.Value, writer);
            writer.Write(";\n");
        }

        public override void WriteMultiplyAssignStatement(MultiplyAssignStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteExpression(n.Target, writer);
            writer.Write(" *= ");
            WriteExpression(n.Value, writer);
            writer.Write(";\n");
        }

        public override void WriteMatrixMultiplyAssignStatement(MatrixMultiplyAssignStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            RuntimeErrorException e = new RuntimeErrorException(SR.CSharpHasNoMatrixMultiplyAssignOp);
            e.AddLocation(n.Context);
            throw e;
        }

        public override void WriteFloorDivideAssignStatement(FloorDivideAssignStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteExpression(n.Target, writer);
            writer.Write(" /= ");
            WriteExpression(n.Value, writer);
            writer.Write(";\n");
        }

        public override void WriteDivideAssignStatement(DivideAssignStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteExpression(n.Target, writer);
            writer.Write(" /= ");
            WriteExpression(Expression.NewAsInstanceOf(n.Value, TypeExpression.NewFloat64()), writer);
            writer.Write(";\n");
        }

        public override void WriteModuloAssignStatement(ModuloAssignStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteExpression(n.Target, writer);
            writer.Write(" %= ");
            WriteExpression(n.Value, writer);
            writer.Write(";\n");
        }

        public override void WritePowerAssignStatement(PowerAssignStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteExpression(n.Target, writer);
            writer.Write(" = ");
            writer.Write("global::System.Math.Pow(");
            WriteExpression(n.Target, writer);
            writer.Write(", ");
            WriteExpression(n.Value, writer);
            writer.Write(")");
            writer.Write(";\n");
        }

        public override void WriteLeftShiftAssignStatement(LeftShiftAssignStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteExpression(n.Target, writer);
            writer.Write(" <<= ");
            WriteExpression(n.Value, writer);
            writer.Write(";\n");
        }

        public override void WriteRightShiftAssignStatement(RightShiftAssignStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteExpression(n.Target, writer);
            writer.Write(" >>= ");
            WriteExpression(n.Value, writer);
            writer.Write(";\n");
        }

        public override void WriteUnsignedRightShiftAssignStatement(UnsignedRightShiftAssignStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteExpression(n.Target, writer);
            writer.Write(" = ");
            var cast1 = Expression.NewAsInstanceOf(n.Target, TypeExpression.NewUInt());
            var shift = Expression.NewRightShift(cast1, n.Value);
            var cast2 = Expression.NewAsInstanceOf(shift, TypeExpression.NewInt());
            WriteExpression(cast2, writer);
            writer.Write(";\n");
        }

        public override void WriteBitwiseAndAssignStatement(BitwiseAndAssignStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteExpression(n.Target, writer);
            writer.Write(" &= ");
            WriteExpression(n.Value, writer);
            writer.Write(";\n");
        }

        public override void WriteBitwiseXorAssignStatement(BitwiseXorAssignStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteExpression(n.Target, writer);
            writer.Write(" ^= ");
            WriteExpression(n.Value, writer);
            writer.Write(";\n");
        }

        public override void WriteBitwiseOrAssignStatement(BitwiseOrAssignStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteExpression(n.Target, writer);
            writer.Write(" |= ");
            WriteExpression(n.Value, writer);
            writer.Write(";\n");
        }

        public override void WriteSequenceStatement(SequenceStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            foreach (Statement item in n.Items)
                WriteStatement(item, writer);
        }

        public override void WriteSimpleStatement(SimpleStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteStatement(n.Body, writer);
        }

        public override void WriteBlockStatement(BlockStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.OpenCurlyBlock();
            foreach (Statement item in n.Body)
                WriteStatement(item, writer);
            writer.CloseCurlyBlock();
        }

        public override void WriteSuiteStatement(SuiteStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("\n");
            if (!n.IsCompound)
                writer.OpenBlock();
            WriteStatement(n.Body, writer);
            if (!n.IsCompound)
                writer.CloseBlock();
        }

        public virtual void WriteBody(Statement body, IndentedWriter writer, bool allowSimplify)
        {
            if (body == null)
                throw new ArgumentNullException(nameof(body));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (body.IsSuite)
            {
                if (body.AsSuite.IsCompound)
                    WriteStatement(body, writer);
                return;
            }
            writer.Write("\n");
            if (allowSimplify)
                writer.OpenBlock();
            else
                writer.OpenCurlyBlock();
            if (body.IsSuite)
                WriteStatement(body.AsSuite.Body, writer);
            else
                WriteStatement(body, writer);
            if (allowSimplify)
                writer.CloseBlock();
            else
                writer.CloseCurlyBlock();
        }

        public override void WriteWithStatement(WithStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (n.IsAsync)
            {
                RuntimeErrorException e = new RuntimeErrorException(SR.CSharpHasNoAsyncWithStatement);
                e.AddLocation(n.Context);
                throw e;
            }
            for (int i = 0; i < n.Bindings.Count; i++)
            {
                WithBinding item = n.Bindings[i];
                if (i > 0)
                    writer.Write("\n");
                if (item.Variables != null && !item.Variables.IsNameReference)
                {
                    RuntimeErrorException e = new RuntimeErrorException(SR.CSharpWithNotSimpleName);
                    e.AddLocation(n.Context);
                    e.AddLocation(item.Context);
                    e.AddLocation(item.Variables.Context);
                    throw e;
                }
                writer.Write("using (");
                if (item.Variables != null)
                {
                    writer.Write("var ");
                    WriteIdentifier(item.Variables.AsNameReference.Name, writer);
                    writer.Write(" = ");
                }
                WriteExpression(item.Value, writer);
                writer.Write(")");
            }
            WriteBody(n.Body, writer, true);
        }

        public override void WriteForStatement(ForStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (n.IsAsync)
            {
                RuntimeErrorException e = new RuntimeErrorException(SR.CSharpHasNoAsyncForStatement);
                e.AddLocation(n.Context);
                throw e;
            }
            writer.Write("foreach (var ");
            if (!n.Enumerator.IsNameReference)
            {
                RuntimeErrorException e = new RuntimeErrorException(SR.CSharpForNotSimpleName);
                e.AddLocation(n.Context);
                throw e;
            }
            WriteIdentifier(n.Enumerator.AsNameReference.Name, writer);
            writer.Write(" in ");
            WriteExpression(n.Enumerable, writer);
            writer.Write(")");
            WriteBody(n.Body, writer, true);
            if (n.OrElse != null)
            {
                RuntimeErrorException e = new RuntimeErrorException(SR.CSharpHasNoForElseStatement);
                e.AddLocation(n.Context);
                throw e;
            }
        }

        public override void WriteWhileStatement(WhileStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("while (");
            WriteExpression(n.Test, writer);
            writer.Write(")");
            WriteBody(n.Body, writer, true);
            if (n.OrElse != null)
            {
                RuntimeErrorException e = new RuntimeErrorException(SR.CSharpHasNoWhileElseStatement);
                e.AddLocation(n.Context);
                throw e;
            }
        }

        public override void WriteIfStatement(IfStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("if (");
            WriteExpression(n.Test, writer);
            writer.Write(")");
            WriteBody(n.Body, writer, true);
            foreach (ElseIfBranch item in n.ElseIfBranches)
            {
                writer.Write("else if (");
                WriteExpression(item.Test, writer);
                writer.Write(")");
                WriteBody(item.Body, writer, true);
            }
            if (n.OrElse != null)
            {
                writer.Write("else");
                WriteBody(n.OrElse, writer, true);
            }
        }

        public override void WriteTryStatement(TryStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("try");
            WriteBody(n.Body, writer, false);
            foreach (ExceptionHandler item in n.Handlers)
            {
                writer.Write("catch");
                if (item.Type != null)
                {
                    writer.Write(" (");
                    WriteTypeExpression(item.Type, writer);
                    if (item.Name != null)
                    {
                        writer.Write(" ");
                        WriteIdentifier(item.Name, writer);
                    }
                    writer.Write(")");
                }
                WriteBody(n.Body, writer, false);
            }
            if (n.OrElse != null)
            {
                RuntimeErrorException e = new RuntimeErrorException(SR.CSharpHasNoTryCatchElseStatement);
                e.AddLocation(n.Context);
                throw e;
            }
            if (n.FinalBody != null)
            {
                writer.Write("finally");
                WriteBody(n.FinalBody, writer, false);
            }
        }

        public override void WriteSwitchStatement(SwitchStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("switch (");
            WriteExpression(n.Value, writer);
            writer.Write(")\n");
            writer.OpenCurlyBlock();
            foreach (SwitchCaseClause clause in n.Clauses)
            {
                writer.Write("case ");
                WriteExpression(clause.Value, writer);
                writer.Write(":");
                if (clause.Body.IsSuite && clause.Body.AsSuite.IsCompound)
                    writer.OpenBlock();
                WriteBody(clause.Body, writer, true);
                if (clause.Body.IsSuite && clause.Body.AsSuite.IsCompound)
                    writer.CloseBlock();
            }
            if (n.DefaultActions != null && ((ISyntaxTreeNode)n.DefaultActions).Count > 0)
            {
                writer.Write("default:");
                if (n.DefaultActions.IsSuite && n.DefaultActions.AsSuite.IsCompound)
                    writer.OpenBlock();
                WriteBody(n.DefaultActions, writer, true);
                if (n.DefaultActions.IsSuite && n.DefaultActions.AsSuite.IsCompound)
                    writer.CloseBlock();
            }
            writer.CloseCurlyBlock();
        }

        private static bool IsParameterStatic(Parameter n)
        {
            return n != null && n.Type != null;
        }

        private static bool IsParameterListStatic(ParameterList n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            foreach (Parameter item in n.PositionalOnly)
                if (IsParameterStatic(item))
                    return true;
            foreach (Parameter item in n.Positional)
                if (IsParameterStatic(item))
                    return true;
            if (IsParameterStatic(n.ExtraPositional))
                return true;
            foreach (Parameter item in n.KeywordOnly)
                if (IsParameterStatic(item))
                    return true;
            if (IsParameterStatic(n.ExtraKeywords))
                return true;
            return false;
        }

        private static ClassMemberDeclarator ConvertFunctionDeclaration(FunctionDeclaration n, bool isInsideClass)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            var a = ClassMemberAccessibility.Public;
            var m = ClassMemberVirtualModifier.Default;
            var p = n.Parameters;
            var r = n.ResultType;
            var b = ClassMethodBody.NewBlock(n.Body);
            var c = n.Context;
            bool isStatic = false;
            if (isInsideClass)
            {
                foreach (Decorator deco in n.Decorators.Items)
                {
                    if (!deco.IsDottedName || deco.AsDottedName.Names.Count != 1)
                        continue;
                    if (deco.AsDottedName.Names[0].Name == "staticmethod")
                    {
                        isStatic = true;
                        break;
                    }
                }
                if (!isStatic)
                {
                    if (p.Positional.Count > 0 && p.Positional[0].Name.Name == "self")
                        p = p.WithPositional(p.Positional.RemoveAt(0));
                }
            }
            if (n.Name.Name.StartsWith("__"))
            {
                if (!n.Name.Name.EndsWith("__"))
                    a = ClassMemberAccessibility.Private;
            }
            else if (n.Name.Name.StartsWith("_"))
            {
                a = ClassMemberAccessibility.InternalProtected;
            }
            if (a != ClassMemberAccessibility.Public && !isInsideClass)
                a = ClassMemberAccessibility.Internal;
            if (r == null && !IsParameterListStatic(p))
                r = TypeExpression.NewDynamic();
            if (!isInsideClass)
                return ClassMemberDeclarator.NewMethod(a, n.IsAsync, true, n.Name, p, r, b, c);
            else if (isStatic)
                return ClassMemberDeclarator.NewMethod(a, n.IsAsync, isStatic, n.Name, p, r, b, c);
            else
                return ClassMemberDeclarator.NewVirtualMethod(a, n.IsAsync, m, n.Name, p, r, b, c);
        }

        private static ClassMemberDeclarator ConvertPropertyDeclaration(AnnotatedAssignStatement n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (!n.Target.IsNameReference || !n.IsSimple)
                return null;
            var a = ClassMemberAccessibility.Public;
            var t = n.Type;
            var v = n.Value;
            var c = n.Context;
            var name = n.Target.AsNameReference.Name;
            if (name.Name.StartsWith("__"))
            {
                if (!name.Name.EndsWith("__"))
                    a = ClassMemberAccessibility.Private;
            }
            else if (name.Name.StartsWith("_"))
            {
                a = ClassMemberAccessibility.InternalProtected;
            }
            return ClassMemberDeclarator.NewPropertyField(a, false, name, true, t, v, c);
        }

        private static ClassMemberDeclarator ConvertClassMember(Statement n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (n.IsFunction)
                return ConvertFunctionDeclaration(n.AsFunction.Declaration, true);
            if (n.IsAnnotatedAssign)
                return ConvertPropertyDeclaration(n.AsAnnotatedAssign);
            return null;
        }

        private static ClassDeclarator ConvertClassDeclaration(ClassDeclaration n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            var bases = new List<TypeExpression>();
            foreach (Argument item in n.Arguments.Items)
            {
                if (!item.IsPositional)
                    continue;
                var v = item.AsPositional.Value;
                if (v.IsBuiltinTypeReference)
                    bases.Add(v.AsBuiltinTypeReference.Type);
                else if (v.IsNameReference)
                    bases.Add(TypeExpression.NewNameReference(v.AsNameReference.Name));
            }
            var members = new List<ClassMemberDeclarator>();
            foreach (Statement item in n.Body)
            {
                var member = ConvertClassMember(item);
                if (member != null)
                    members.Add(member);
            }
            var i = n.Name.Name.StartsWith("_");
            var b = Sequence.New(bases);
            var m = Sequence.New(members);
            var c = n.Context;
            return ClassDeclarator.New(i, ClassVirtualModifier.Default, n.Name, b, m, c);
        }

        public override void WriteFunctionStatement(FunctionStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            var m = ConvertFunctionDeclaration(n.Declaration, false);
            WriteMethodClassMemberDeclarator(m.AsMethod, writer);
        }

        public override void WriteClassStatement(ClassStatement n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            var c = ConvertClassDeclaration(n.Declaration);
            WriteClassDeclarator(c, writer);
        }

        public override void WriteFunctionDeclaration(FunctionDeclaration n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteDecoratorList(n.Decorators, writer);
            if (n.IsAsync)
                writer.Write("async ");
            writer.Write("def ");
            WriteIdentifier(n.Name, writer);
            writer.Write("(");
            WriteParameterList(n.Parameters, writer);
            writer.Write(")");
            if (n.ResultType != null)
            {
                writer.Write(" -> ");
                WriteTypeExpression(n.ResultType, writer);
            }
            WriteBody(n.Body, writer, false);
        }

        public override void WriteEnumDeclarator(EnumDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("[global::System.Serializable]\n");
            writer.Write(n.IsInternal ? "internal" : "public");
            writer.Write(" enum ");
            WriteIdentifier(n.Name, writer);
            writer.Write("\n");
            writer.OpenCurlyBlock();
            if (n.IsReversed)
            {
                for (int i = n.Items.Count - 1; i >= 0; i--)
                {
                    EnumItemDeclarator item = n.Items[i];
                    for (int j = item.Names.Count - 1; j >= 0; j--)
                    {
                        if (j < item.Names.Count - 1)
                            writer.Write(", ");
                        WriteIdentifier(item.Names[j], writer);
                    }
                    writer.Write(",\n");
                }
            }
            else
            {
                for (int i = 0; i < n.Items.Count; i++)
                {
                    EnumItemDeclarator item = n.Items[i];
                    for (int j = 0; j < item.Names.Count; j++)
                    {
                        if (j > 0)
                            writer.Write(", ");
                        WriteIdentifier(item.Names[j], writer);
                    }
                    writer.Write(",\n");
                }
            }
            writer.CloseCurlyBlock();
        }

        public override void WriteEnumTypeDeclarator(EnumDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteEnumDeclarator(n, writer);
        }

        public override void WriteParameter(Parameter n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (n.Type != null)
            {
                WriteTypeExpression(n.Type, writer);
                writer.Write(" ");
            }
            WriteIdentifier(n.Name, writer);
            if (n.Value != null)
            {
                writer.Write(" = ");
                WriteExpression(n.Value, writer);
            }
        }

        public override void WriteEventInterfaceMemberDeclarator(EventInterfaceMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("event ");
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            WriteIdentifier(n.Name, writer);
            writer.Write(";");
            writer.Write("\n");
        }

        public override void WritePropertyInterfaceMemberDeclarator(PropertyInterfaceMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            WriteIdentifier(n.Name, writer);
            writer.Write(" { get;");
            if (n.IsMutable)
                writer.Write(" set;");
            writer.Write(" }");
            writer.Write("\n");
        }

        public override void WriteIndexerInterfaceMemberDeclarator(IndexerInterfaceMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteTypeExpression(n.Type, writer);
            writer.Write(" this[");
            WriteParameterList(n.Parameters, writer);
            writer.Write("] { get;");
            if (n.IsMutable)
                writer.Write(" set;");
            writer.Write(" }");
            writer.Write("\n");
        }

        public override void WriteMethodInterfaceMemberDeclarator(MethodInterfaceMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (n.ResultType == null)
                writer.Write("void");
            else
                WriteTypeExpression(n.ResultType, writer);
            writer.Write(" ");
            WriteIdentifier(n.Name, writer);
            writer.Write("(");
            WriteParameterList(n.Parameters, writer);
            writer.Write(");");
            writer.Write("\n");
        }

        public override void WriteInterfaceDeclarator(InterfaceDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write(n.IsInternal ? "internal" : "public");
            writer.Write(" partial interface ");
            WriteIdentifier(n.Name, writer);
            if (n.Bases.Count > 0)
            {
                writer.Write(" : ");
                for (int i = 0; i < n.Bases.Count; i++)
                {
                    if (i > 0)
                        writer.Write(", ");
                    WriteTypeExpression(n.Bases[i], writer);
                };
            }
            writer.Write("\n");
            writer.OpenCurlyBlock();
            foreach (InterfaceMemberDeclarator item in n.Members)
                WriteInterfaceMemberDeclarator(item, writer);
            writer.CloseCurlyBlock();
        }

        public override void WriteClassMemberVirtualModifier(ClassMemberVirtualModifier n, IndentedWriter writer)
        {
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            switch (n)
            {
                case ClassMemberVirtualModifier.Default: writer.Write("virtual"); break;
                case ClassMemberVirtualModifier.Override: writer.Write("override"); break;
                case ClassMemberVirtualModifier.Sealed: writer.Write("sealed override"); break;
                default:
                    throw new ArgumentException(SR.InvalidClassMemberVirtualModifier, nameof(n));
            }
        }

        public override void WriteExprClassMethodBody(ExprClassMethodBody n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write(" => ");
            WriteExpression(n.Value, writer);
            writer.Write(";\n");
        }

        public override void WriteBlockClassMethodBody(BlockClassMethodBody n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteStatement(n.Actions, writer);
        }

        public override void WriteFieldClassMemberDeclarator(FieldClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteClassMemberAccessibility(n.Access, writer);
            writer.Write(" ");
            if (n.IsStatic)
                writer.Write("static ");
            if (!n.IsMutable)
                writer.Write("readonly ");
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            WriteIdentifier(n.Name, writer);
            if (n.Value != null)
            {
                writer.Write(" = ");
                WriteExpression(n.Value, writer);
            }
            writer.Write(";\n");
        }

        public override void WriteEventClassMemberDeclarator(EventClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteClassMemberAccessibility(n.Access, writer);
            writer.Write(" ");
            if (n.IsStatic)
                writer.Write("static ");
            writer.Write("event ");
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            WriteIdentifier(n.Name, writer);
            writer.Write(";\n");
        }

        public override void WriteEventBlockClassMemberDeclarator(EventBlockClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteClassMemberAccessibility(n.Access, writer);
            writer.Write(" ");
            if (n.IsStatic)
                writer.Write("static ");
            writer.Write("event ");
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            WriteIdentifier(n.Name, writer);
            writer.Write("\n");
            writer.OpenCurlyBlock();
            writer.Write("add");
            WriteClassMethodBody(n.Adder, writer);
            if (n.Remover != null)
            {
                writer.Write("remove");
                WriteClassMethodBody(n.Remover, writer);
            }
            writer.CloseCurlyBlock();
        }

        public override void WriteVirtualEventClassMemberDeclarator(VirtualEventClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteClassMemberAccessibility(n.Access, writer);
            writer.Write(" ");
            WriteClassMemberVirtualModifier(n.Modifier, writer);
            writer.Write(" ");
            writer.Write("event ");
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            WriteIdentifier(n.Name, writer);
            writer.Write(";\n");
        }

        public override void WriteVirtualEventBlockClassMemberDeclarator(VirtualEventBlockClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteClassMemberAccessibility(n.Access, writer);
            writer.Write(" ");
            WriteClassMemberVirtualModifier(n.Modifier, writer);
            writer.Write(" ");
            writer.Write("event ");
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            WriteIdentifier(n.Name, writer);
            writer.Write("\n");
            writer.OpenCurlyBlock();
            writer.Write("add");
            WriteClassMethodBody(n.Adder, writer);
            if (n.Remover != null)
            {
                writer.Write("remove");
                WriteClassMethodBody(n.Remover, writer);
            }
            writer.CloseCurlyBlock();
        }

        public override void WriteAbstractEventClassMemberDeclarator(AbstractEventClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteClassMemberAccessibility(n.Access, writer);
            writer.Write(" ");
            writer.Write("abstract ");
            writer.Write("event ");
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            WriteIdentifier(n.Name, writer);
            writer.Write(";\n");
        }

        public override void WriteExplicitEventImplClassMemberDeclarator(ExplicitEventImplClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            var eventRef = Expression.NewNameReference(n.Name);
            var valueRef = Expression.NewNameReference(Identifier.New("value"));
            var adder = ClassMethodBody.NewBlock(Statement.NewAddAssign(eventRef, valueRef));
            var remover = ClassMethodBody.NewBlock(Statement.NewSubtractAssign(eventRef, valueRef));
            writer.Write("private ");
            writer.Write("event ");
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            WriteIdentifier(n.Name, writer);
            writer.Write(";\n");
            writer.Write("event ");
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            WriteTypeExpression(n.Target, writer);
            writer.Write(".");
            WriteIdentifier(n.Name, writer);
            writer.Write("\n");
            writer.OpenCurlyBlock();
            writer.Write("add");
            WriteClassMethodBody(adder, writer);
            writer.Write("remove");
            WriteClassMethodBody(remover, writer);
            writer.CloseCurlyBlock();
        }

        public override void WriteExplicitEventImplBlockClassMemberDeclarator(ExplicitEventImplBlockClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("event ");
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            WriteTypeExpression(n.Target, writer);
            writer.Write(".");
            WriteIdentifier(n.Name, writer);
            writer.Write("\n");
            writer.OpenCurlyBlock();
            writer.Write("add");
            WriteClassMethodBody(n.Adder, writer);
            if (n.Remover != null)
            {
                writer.Write("remove");
                WriteClassMethodBody(n.Remover, writer);
            }
            writer.CloseCurlyBlock();
        }

        public override void WritePropertyClassMemberDeclarator(PropertyClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteClassMemberAccessibility(n.Access, writer);
            writer.Write(" ");
            if (n.IsStatic)
                writer.Write("static ");
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            WriteIdentifier(n.Name, writer);
            writer.Write(" => ");
            WriteExpression(n.Value, writer);
            writer.Write(";\n");
        }

        public override void WritePropertyFieldClassMemberDeclarator(PropertyFieldClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteClassMemberAccessibility(n.Access, writer);
            writer.Write(" ");
            if (n.IsStatic)
                writer.Write("static ");
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            WriteIdentifier(n.Name, writer);
            writer.Write(" { get;");
            if (n.IsMutable)
                writer.Write(" set;");
            writer.Write(" }");
            if (n.Value != null)
            {
                writer.Write(" = ");
                WriteExpression(n.Value, writer);
                writer.Write(";");
            }
            writer.Write("\n");
        }

        public override void WritePropertyBlockClassMemberDeclarator(PropertyBlockClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteClassMemberAccessibility(n.Access, writer);
            writer.Write(" ");
            if (n.IsStatic)
                writer.Write("static ");
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            WriteIdentifier(n.Name, writer);
            writer.Write("\n");
            writer.OpenCurlyBlock();
            writer.Write("get");
            WriteClassMethodBody(n.Getter, writer);
            if (n.Setter != null)
            {
                writer.Write("set");
                WriteClassMethodBody(n.Setter, writer);
            }
            writer.CloseCurlyBlock();
        }

        public override void WriteVirtualPropertyClassMemberDeclarator(VirtualPropertyClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteClassMemberAccessibility(n.Access, writer);
            writer.Write(" ");
            WriteClassMemberVirtualModifier(n.Modifier, writer);
            writer.Write(" ");
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            WriteIdentifier(n.Name, writer);
            writer.Write(" => ");
            WriteExpression(n.Value, writer);
            writer.Write(";\n");
        }

        public override void WriteVirtualPropertyFieldClassMemberDeclarator(VirtualPropertyFieldClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteClassMemberAccessibility(n.Access, writer);
            writer.Write(" ");
            WriteClassMemberVirtualModifier(n.Modifier, writer);
            writer.Write(" ");
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            WriteIdentifier(n.Name, writer);
            writer.Write(" { get;");
            if (n.IsMutable)
                writer.Write(" set;");
            writer.Write(" }");
            if (n.Value != null)
            {
                writer.Write(" = ");
                WriteExpression(n.Value, writer);
                writer.Write(";");
            }
            writer.Write("\n");
        }

        public override void WriteVirtualPropertyBlockClassMemberDeclarator(VirtualPropertyBlockClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteClassMemberAccessibility(n.Access, writer);
            writer.Write(" ");
            WriteClassMemberVirtualModifier(n.Modifier, writer);
            writer.Write(" ");
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            WriteIdentifier(n.Name, writer);
            writer.Write("\n");
            writer.OpenCurlyBlock();
            writer.Write("get");
            WriteClassMethodBody(n.Getter, writer);
            if (n.Setter != null)
            {
                writer.Write("set");
                WriteClassMethodBody(n.Setter, writer);
            }
            writer.CloseCurlyBlock();
        }

        public override void WriteAbstractPropertyClassMemberDeclarator(AbstractPropertyClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteClassMemberAccessibility(n.Access, writer);
            writer.Write(" ");
            writer.Write("abstract ");
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            WriteIdentifier(n.Name, writer);
            writer.Write(" { get;");
            if (n.IsMutable)
                writer.Write(" set;");
            writer.Write(" }");
            writer.Write("\n");
        }

        public override void WriteExplicitPropertyImplClassMemberDeclarator(ExplicitPropertyImplClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            WriteTypeExpression(n.Target, writer);
            writer.Write(".");
            WriteIdentifier(n.Name, writer);
            writer.Write(" => ");
            WriteExpression(n.Value, writer);
            writer.Write(";\n");
        }

        public override void WriteExplicitPropertyImplFieldClassMemberDeclarator(ExplicitPropertyImplFieldClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            WriteTypeExpression(n.Target, writer);
            writer.Write(".");
            WriteIdentifier(n.Name, writer);
            writer.Write(" { get;");
            if (n.IsMutable)
                writer.Write(" set;");
            writer.Write(" }");
            if (n.Value != null)
            {
                writer.Write(" = ");
                WriteExpression(n.Value, writer);
                writer.Write(";");
            }
            writer.Write("\n");
        }

        public override void WriteExplicitPropertyImplBlockClassMemberDeclarator(ExplicitPropertyImplBlockClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            WriteTypeExpression(n.Target, writer);
            writer.Write(".");
            WriteIdentifier(n.Name, writer);
            writer.Write("\n");
            writer.OpenCurlyBlock();
            writer.Write("get");
            WriteClassMethodBody(n.Getter, writer);
            if (n.Setter != null)
            {
                writer.Write("set");
                WriteClassMethodBody(n.Setter, writer);
            }
            writer.CloseCurlyBlock();
        }

        public override void WriteIndexerClassMemberDeclarator(IndexerClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteClassMemberAccessibility(n.Access, writer);
            writer.Write(" ");
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            writer.Write("this[");
            WriteParameterList(n.Parameters, writer);
            writer.Write("]");
            writer.Write(" => ");
            WriteExpression(n.Value, writer);
            writer.Write(";\n");
        }

        public override void WriteIndexerBlockClassMemberDeclarator(IndexerBlockClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteClassMemberAccessibility(n.Access, writer);
            writer.Write(" ");
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            writer.Write("this[");
            WriteParameterList(n.Parameters, writer);
            writer.Write("]");
            writer.Write("\n");
            writer.OpenCurlyBlock();
            writer.Write("get");
            WriteClassMethodBody(n.Getter, writer);
            if (n.Setter != null)
            {
                writer.Write("set");
                WriteClassMethodBody(n.Setter, writer);
            }
            writer.CloseCurlyBlock();
        }

        public override void WriteVirtualIndexerClassMemberDeclarator(VirtualIndexerClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteClassMemberAccessibility(n.Access, writer);
            writer.Write(" ");
            WriteClassMemberVirtualModifier(n.Modifier, writer);
            writer.Write(" ");
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            writer.Write("this[");
            WriteParameterList(n.Parameters, writer);
            writer.Write("]");
            writer.Write(" => ");
            WriteExpression(n.Value, writer);
            writer.Write(";\n");
        }

        public override void WriteVirtualIndexerBlockClassMemberDeclarator(VirtualIndexerBlockClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteClassMemberAccessibility(n.Access, writer);
            writer.Write(" ");
            WriteClassMemberVirtualModifier(n.Modifier, writer);
            writer.Write(" ");
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            writer.Write("this[");
            WriteParameterList(n.Parameters, writer);
            writer.Write("]");
            writer.Write("\n");
            writer.OpenCurlyBlock();
            writer.Write("get");
            WriteClassMethodBody(n.Getter, writer);
            if (n.Setter != null)
            {
                writer.Write("set");
                WriteClassMethodBody(n.Setter, writer);
            }
            writer.CloseCurlyBlock();
        }

        public override void WriteAbstractIndexerClassMemberDeclarator(AbstractIndexerClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteClassMemberAccessibility(n.Access, writer);
            writer.Write(" ");
            writer.Write("abstract ");
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            writer.Write("this[");
            WriteParameterList(n.Parameters, writer);
            writer.Write("]");
            writer.Write(" { get;");
            if (n.IsMutable)
                writer.Write(" set;");
            writer.Write(" }");
            writer.Write("\n");
        }

        public override void WriteExplicitIndexerImplClassMemberDeclarator(ExplicitIndexerImplClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            WriteTypeExpression(n.Target, writer);
            writer.Write(".");
            writer.Write("this[");
            WriteParameterList(n.Parameters, writer);
            writer.Write("]");
            writer.Write(" => ");
            WriteExpression(n.Value, writer);
            writer.Write(";\n");
        }

        public override void WriteExplicitIndexerImplBlockClassMemberDeclarator(ExplicitIndexerImplBlockClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteTypeExpression(n.Type, writer);
            writer.Write(" ");
            WriteTypeExpression(n.Target, writer);
            writer.Write(".");
            writer.Write("this[");
            WriteParameterList(n.Parameters, writer);
            writer.Write("]");
            writer.Write("\n");
            writer.OpenCurlyBlock();
            writer.Write("get");
            WriteClassMethodBody(n.Getter, writer);
            if (n.Setter != null)
            {
                writer.Write("set");
                WriteClassMethodBody(n.Setter, writer);
            }
            writer.CloseCurlyBlock();
        }

        public override void WriteMethodClassMemberDeclarator(MethodClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteClassMemberAccessibility(n.Access, writer);
            writer.Write(" ");
            if (n.IsAsync)
                writer.Write("async ");
            if (n.IsStatic)
                writer.Write("static ");
            if (n.ResultType == null)
                writer.Write("void");
            else
                WriteTypeExpression(n.ResultType, writer);
            writer.Write(" ");
            WriteIdentifier(n.Name, writer);
            writer.Write("(");
            WriteParameterList(n.Parameters, writer);
            writer.Write(")");
            WriteClassMethodBody(n.Body, writer);
        }

        public override void WriteVirtualMethodClassMemberDeclarator(VirtualMethodClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteClassMemberAccessibility(n.Access, writer);
            writer.Write(" ");
            if (n.IsAsync)
                writer.Write("async ");
            WriteClassMemberVirtualModifier(n.Modifier, writer);
            writer.Write(" ");
            if (n.ResultType == null)
                writer.Write("void");
            else
                WriteTypeExpression(n.ResultType, writer);
            writer.Write(" ");
            WriteIdentifier(n.Name, writer);
            writer.Write("(");
            WriteParameterList(n.Parameters, writer);
            writer.Write(")");
            WriteClassMethodBody(n.Body, writer);
        }

        public override void WriteAbstractMethodClassMemberDeclarator(AbstractMethodClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteClassMemberAccessibility(n.Access, writer);
            writer.Write(" ");
            writer.Write("abstract ");
            if (n.ResultType == null)
                writer.Write("void");
            else
                WriteTypeExpression(n.ResultType, writer);
            writer.Write(" ");
            WriteIdentifier(n.Name, writer);
            writer.Write("(");
            WriteParameterList(n.Parameters, writer);
            writer.Write(")");
            writer.Write(";\n");
        }

        public override void WriteExplicitMethodImplClassMemberDeclarator(ExplicitMethodImplClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            if (n.IsAsync)
                writer.Write("async ");
            if (n.ResultType == null)
                writer.Write("void");
            else
                WriteTypeExpression(n.ResultType, writer);
            writer.Write(" ");
            WriteTypeExpression(n.Target, writer);
            writer.Write(".");
            WriteIdentifier(n.Name, writer);
            writer.Write("(");
            WriteParameterList(n.Parameters, writer);
            writer.Write(")");
            WriteClassMethodBody(n.Body, writer);
        }

        public override void WriteConstructorClassMemberDeclarator(ConstructorClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteClassMemberAccessibility(n.Access, writer);
            writer.Write(" ");
            if (_currentClassDeclarator == null)
                writer.Write(".ctor");
            else
                WriteIdentifier(_currentClassDeclarator.Name, writer);
            writer.Write("(");
            WriteParameterList(n.Parameters, writer);
            writer.Write(")");
            WriteClassMethodBody(n.Body, writer);
        }

        public override void WriteConstructorWithThisClassMemberDeclarator(ConstructorWithThisClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteClassMemberAccessibility(n.Access, writer);
            writer.Write(" ");
            if (_currentClassDeclarator == null)
                writer.Write(".ctor");
            else
                WriteIdentifier(_currentClassDeclarator.Name, writer);
            writer.Write("(");
            WriteParameterList(n.Parameters, writer);
            writer.Write(")");
            writer.Write("\n");
            writer.OpenBlock();
            writer.Write(": this");
            WriteArgumentList(n.Arguments, writer);
            writer.CloseBlock();
            WriteClassMethodBody(n.Body, writer);
        }

        public override void WriteConstructorWithBaseClassMemberDeclarator(ConstructorWithBaseClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            WriteClassMemberAccessibility(n.Access, writer);
            writer.Write(" ");
            if (_currentClassDeclarator == null)
                writer.Write(".ctor");
            else
                WriteIdentifier(_currentClassDeclarator.Name, writer);
            writer.Write("(");
            WriteParameterList(n.Parameters, writer);
            writer.Write(")");
            writer.Write("\n");
            writer.OpenBlock();
            writer.Write(": base");
            WriteArgumentList(n.Arguments, writer);
            writer.CloseBlock();
            WriteClassMethodBody(n.Body, writer);
        }

        public override void WriteStaticConstructorClassMemberDeclarator(StaticConstructorClassMemberDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("static");
            writer.Write(" ");
            if (_currentClassDeclarator == null)
                writer.Write(".ctor");
            else
                WriteIdentifier(_currentClassDeclarator.Name, writer);
            writer.Write("(");
            writer.Write(")");
            WriteClassMethodBody(n.Body, writer);
        }

        protected override bool ClassMemberHasComplexBody(ClassMemberDeclarator n)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (n.IsExplicitEventImpl)
                return true;
            return base.ClassMemberHasComplexBody(n);
        }

        public override void WriteClassDeclarator(ClassDeclarator n, IndentedWriter writer)
        {
            if (n == null)
                throw new ArgumentNullException(nameof(n));
            if (writer == null)
                throw new ArgumentNullException(nameof(writer));
            writer.Write("[global::System.Serializable]\n");
            writer.Write(n.IsInternal ? "internal" : "public");
            if (n.Modifier == ClassVirtualModifier.Struct)
                writer.Write(" partial struct ");
            else
            {
                if (n.Modifier != ClassVirtualModifier.Default)
                {
                    writer.Write(" ");
                    WriteClassVirtualModifier(n.Modifier, writer);
                }
                writer.Write(" partial class ");
            }
            WriteIdentifier(n.Name, writer);
            if (n.Bases.Count > 0)
            {
                writer.Write(" : ");
                for (int i = 0; i < n.Bases.Count; i++)
                {
                    if (i > 0)
                        writer.Write(", ");
                    WriteTypeExpression(n.Bases[i], writer);
                };
            }
            writer.Write("\n");
            writer.OpenCurlyBlock();
            ClassDeclarator saved = _currentClassDeclarator;
            _currentClassDeclarator = n;
            for (int i = 0; i < n.Members.Count; i++)
            {
                ClassMemberDeclarator item = n.Members[i];
                if (i > 0)
                {
                    bool flag1 = ClassMemberHasComplexBody(item);
                    bool flag2 = ClassMemberHasComplexBody(n.Members[i - 1]);
                    if (flag1 || flag1 != flag2)
                        writer.Write("\n");
                }
                WriteClassMemberDeclarator(item, writer);
            }
            _currentClassDeclarator = saved;
            writer.CloseCurlyBlock();
        }

        public static void Test()
        {
            ExceptionExtensions.Protect(() =>
            {
                GrammarDeclaration g = GrammarDeclaration.LoadFromFile(@"..\..\..\docs\Grammar14.g");
                string str = g.ToString(Instance);
                if (!string.IsNullOrWhiteSpace(str))
                    Console.WriteLine(str);
            });
        }

        public static void TestWriter()
        {
            while (true)
            {
                SourceReader reader = SourceReader.FromConsole();
                if (reader.Peek() == -1)
                    break;
                try
                {
                    ProgramDescriptionParser parser = new ProgramDescriptionParser(reader);
                    ProgramDescription ast = parser.Parse();
                    string str = ast.ToString(Instance);
                    if (!string.IsNullOrWhiteSpace(str))
                        Console.WriteLine(str);
                }
                catch (Exception e)
                {
                    e.Print();
                }
            }
        }
    }
}
